//
// nfldr.cpp
// Usage: The NewLife OS Loader, using Ext2fs
// Create Date: 2009-01-31
// Creator: MaJiuyue
//
#include <NewLife.h>
#include "protect.h"
#include "Multiboot.h"
#include "bootutil.h"
#include "knlvar.h"
#include "../pageman/PageTable.h"
#include "../pageman/PageFrameAllocator.h"
#include "../schedule/Process.h"

// Init PD and PT properly, setup up page
void SetupPage(multiboot_info_t * mbi);

extern "C"
void nfldr( multiboot_info_t * mbi, unsigned int magic )
{
    init_video();
    kprintf("Booting NewLife OS Kernel.....\n");

    /* Check if Multiboot-compliant boot loader */
    if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
    {
        kprintf ("Invalid magic number: 0x%x\n", (unsigned) magic);
        return;
    }

    /* Save multiboot_info into KernelGlobalVar */
    // ==VAR: low memory==
    LOWMEM_START = 0x1000;
    LOWMEM_END = ((u32)mbi->mem_lower)*1024 - 1;
    // ==VAR: KERNEL_END==
    if(CHECK_FLAG(mbi->flags, 3))
        KERNEL_END = ((module_t *)mbi->mods_addr)[mbi->mods_count-1].mod_end;
    else
    {
        extern char knl_end;
        KERNEL_END = (u32)(&knl_end);
    }
    KERNEL_END = (KERNEL_END+0xFFF) & 0xFFFFF000;

    // Support maxium memory size: 512M
    if(mbi->mem_upper*1024 > MAXIMUM_MEMORY_SIZE-0x100000)
    {
        kprintf("Memory size exceeding maximum size supported.\n");
        PHYMAP_END = MAXIMUM_MEMORY_SIZE;
    }

    else
        PHYMAP_END = ((u32)(mbi->mem_upper))*1024 + 0x100000;

kprintf("PHYMAP_END: 0x%x\n", PHYMAP_END);

	/* Are mmap_* valid? */
       if (CHECK_FLAG (mbi->flags, 6))
         {
           memory_map_t *mmap;
     
           kprintf ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
                   (unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
           for (mmap = (memory_map_t *) mbi->mmap_addr;
                (unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
                mmap = (memory_map_t *) ((unsigned long) mmap
                                         + mmap->size + sizeof (mmap->size)))
             kprintf (" size = 0x%x, base_addr = 0x%x%x,"
                     " length = 0x%x%x, type = 0x%x\n",
                     (unsigned) mmap->size,
                     (unsigned) mmap->base_addr_high,
                     (unsigned) mmap->base_addr_low,
                     (unsigned) mmap->length_high,
                     (unsigned) mmap->length_low,
                     (unsigned) mmap->type);
         }



    // ==VAR: KERNEL_RESERVED==
    KERNEL_RESERVED_START = 0x1000000;
    KERNEL_RESERVED_END = KERNEL_RESERVED_START +
        ((PHYMAP_END/PAGE_SIZE)*sizeof(Page) + KERNEL_RESERVED_SIZE)&0xFFFFF000;

    // ==VAR: VM==
    VMALLOC_START = PHYMAP_END + 0x800000;
    VMALLOC_END = 0x3FCFE000; 

    // ==VAR: TKMAP_BASE==
    TKMAP_BASE = 0x3FD00000;
    
    // ==VAR: PKMAP_BASE==
    PKMAP_BASE = 0x3FE00000;

    // ==VAR: FIXMAP_BASE==
    FIXADDR_BASE = 0x4FF00000; 

    // ==VAR: boot_device==
    boot_dev = mbi->boot_device;

    // ==VAR: module information==
    mods_addr = (addr_t)mbi->mods_addr;
    mods_count = (int)mbi->mods_count;

    /* Setup Page */ 
    SetupPage(mbi);

    kprintf("Memory Size: %dMB\n", PHYMAP_END/(1024*1024));
}    

void SetupPage(multiboot_info_t * mbi)
{
	// Make PageTable for first 8M of 3G, PSE-enable, 4M page
	for(int i=0; i<1024; i++)
		KernelPDT[i] = 0 | 2;
	KernelPDT[0] = KernelPDT[768] = 0x1E3;
	KernelPDT[1] = KernelPDT[769] = 0x4001E3;

	// Load GDT and PageDirectory
	__asm__ __volatile__("lgdt %0":"=m"(GdtPtr));
	__asm__ __volatile__("mov %%eax, %%cr3"::"a"(KernelPDT));
	// First enable PSE
	__asm__ __volatile__(
		"mov %cr4, %eax\n\t"
		"or $0x10, %eax\n\t"
		"mov %eax, %cr4"
	);
	// Enable ProtectedMode and Paging
	__asm__ __volatile__(
	        "mov %cr0, %eax\n\t"
	        "or $0x80000001, %eax\n\t"
	        "mov %eax, %cr0"
	);
	// Do a long jmp
	__asm__ __volatile__("ljmp $0x8,$reload_segments");
	__asm__ __volatile__("reload_segments:");
	__asm__ __volatile__(
	        "mov $0x10, %ax\n\t"
	        "mov %ax, %ds\n\t"
	        "mov %ax, %es\n\t"
	        "mov %ax, %fs\n\t"
	        "mov %ax, %gs\n\t"
	        "mov %ax, %ss\n\t"
	);
	
	// Restore first 8M to non-paging
	*(u32 *)(&KernelPDT[0]) = *(u32 *)(&KernelPDT[1]) = 2; 
	PageTable::InitializeKPD(KernelPDT);
}

